package com.jinke.api.modules.api.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.jinke.api.common.api.CommonResult;
import com.jinke.api.common.service.RedisService;
import com.jinke.api.common.util.CommonUtil;
import com.jinke.api.modules.api.controller.resp.GetChannelResult;
import com.jinke.api.modules.app.enums.ChannelStatus;
import com.jinke.api.modules.app.enums.OrderStatus;
import com.jinke.api.modules.app.enums.OrderType;
import com.jinke.api.modules.app.model.Order;
import com.jinke.api.modules.app.model.OrderParcel;
import com.jinke.api.modules.app.request.CancelLabelRequest;
import com.jinke.api.modules.app.request.TrackingRequest;
import com.jinke.api.modules.app.service.OrderParcelService;
import com.jinke.api.modules.app.service.OrderService;
import com.jinke.api.modules.base.service.BaseShipService;
import com.jinke.api.modules.base.service.ShipFactory;
import com.jinke.api.modules.third.ezeeship.model.request.EstimateRateRequest;
import com.jinke.api.modules.third.ezeeship.model.response.CreateLabelResponse;
import com.jinke.api.modules.third.ezeeship.model.response.EstimateRateResponse;
import com.jinke.api.modules.third.ezeeship.model.response.TrackingResponse;
import com.jinke.api.modules.third.ezeeship.util.EzeeShipUtil;
import com.jinke.api.modules.ums.model.Channel;
import com.jinke.api.modules.ums.model.ChannelCarrier;
import com.jinke.api.modules.ums.model.UserChannel;
import com.jinke.api.modules.ums.service.ChannelCarrierService;
import com.jinke.api.modules.ums.service.ChannelService;
import com.jinke.api.modules.ums.service.UserChannelService;
import com.jinke.api.modules.ums.vo.UserChannelVO;
import com.jinke.api.security.util.ApiUserUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 承运商平台 前端控制器
 * </p>
 *
 * @author macro
 * @since 2024-01-15
 */
@RestController
@Api(tags = "开放API")
@Tag(name = "开放API", description = "开放API")
@RequestMapping("/api/openapi")
@Slf4j
public class ApiController {

    @Resource
    private OrderService orderService;
    @Resource
    private OrderParcelService orderParcelService;
    @Resource
    private UserChannelService userChannelService;
    @Resource
    private ChannelService channelService;
    @Autowired
    private ChannelCarrierService channelCarrierService;
    @Resource
    private ShipFactory shipFactory;
    @Autowired
    private RedisService redisService;
    @Autowired
    private EzeeShipUtil ezeeShipUtil;

    @ApiOperation("费率估算")
    @RequestMapping(value = "/shipment/estimateRate", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<EstimateRateResponse.Data> estimateRate(@RequestBody EstimateRateRequest request) {
        //统一处理数据
        CommonUtil.handle(request);
        // 调用接口
        request.setIsTest(false);
        log.info(JSONUtil.toJsonStr(request));
        // 判断是否拥有权限
        String serviceCode = request.getServiceCode();
        Integer userId = ApiUserUtil.getUserId();
        List<UserChannel> userChannels = userChannelService.list(new LambdaQueryWrapper<UserChannel>()
                .eq(UserChannel::getUserId, userId)
        );
        if (CollectionUtil.isEmpty(userChannels)) {
            return CommonResult.failed("Permission denied");
        }
        Set<Integer> channelIds = userChannels.stream().map(UserChannel::getChannelId).collect(Collectors.toSet());
        // 走API 的话  传渠道名称
        Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>()
                .eq(Channel::getChannelName, serviceCode)
//                .eq(Channel::getStatus, ChannelStatus.ON)
                .in(Channel::getId, channelIds)
        );
        if (channel == null) {
            return CommonResult.failed("channel not found ,please check your serviceCode,it should be like UPS_2ndDay ID:4567 ");
        }
        request.setChannelId(channel.getId());

        String orderNO = request.getOrderNO();
        if (StringUtils.isBlank(orderNO)) {
            String orderNo = CommonUtil.getOrderNo();
            request.setOrderNO(orderNo);
        }

        UserChannel userChannel = userChannels.stream().filter(x -> {
            return x.getChannelId() == channel.getId().intValue();
        }).findFirst().get();
        UserChannelVO userChannelVO = new UserChannelVO();
        BeanUtils.copyProperties(userChannel, userChannelVO);
        userChannelVO.setChannel(channel);

        request.setUserChannelVO(userChannelVO);
        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
        return baseShipService.estimateRate(request,userId);
    }

    @ApiOperation("生成面单")
    @RequestMapping(value = "/label/create", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<CreateLabelResponse.Data> createLabel(@RequestBody @Validated  EstimateRateRequest request) {
        //统一处理数据
        CommonUtil.handle(request);
        String orderNO = request.getOrderNO();
        log.info("API生成面单{}-{}", orderNO, JSONUtil.toJsonStr(request));
        // 调用接口
        request.setIsTest(false);

        if (StringUtils.isNotBlank(orderNO)) {
            String key = "cache" + orderNO;
            Boolean has = redisService.hasKey(key);

            if (!has) {
                log.info("加缓存{},{}", orderNO, key);
                redisService.lPush(key, 0, 5);

            } else {
                log.info("高频访问{},{}", orderNO, JSONUtil.toJsonStr(request));
                CommonResult commonResult = new CommonResult(444, "正在获取单号中请勿重复提交", request);
                return commonResult;
            }
        }

        //检查订单号 不可重复
        Integer integer = orderService.checkOrderNoNoRepeat(request);

        if (integer == 1) {
            log.info("已经存在的订单再次创建{}-{}", orderNO, JSONUtil.toJsonStr(request));
            CommonResult commonResult = new CommonResult(555, "正在获取单号请勿重复提交", request);
            return commonResult;
        }


        // serviceCode对应channel表中的channelName字段
        String serviceCode = request.getServiceCode();
        Integer userId = ApiUserUtil.getUserId();
        //获取当前用户授权渠道
        List<UserChannel> userChannels = userChannelService.list(new LambdaQueryWrapper<UserChannel>()
                .eq(UserChannel::getUserId, userId)
        );
        if (CollectionUtil.isEmpty(userChannels)) {
            //授权渠道为空
            return CommonResult.failed(userId + "The authorization channel is empty");
        }
        Set<Integer> channelIds = userChannels.stream().map(UserChannel::getChannelId).collect(Collectors.toSet());
        // 走API 的话  传渠道名称
        Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>()
                        .eq(Channel::getChannelName, serviceCode)
//                .eq(Channel::getStatus, ChannelStatus.ON)
                        .in(Channel::getId, channelIds)
        );
        if (channel == null) {
            //对应的渠道已经关闭或不存在
            return CommonResult.failed(serviceCode + " channel has been closed or does not exist");
        }
        //设置主键 在下单时用它查channel取channelCode给第三方平台调接口时当参数
        request.setChannelId(channel.getId());

        //获取匹配参数中渠道NAME且可用的被授权的渠道
        UserChannel userChannel = userChannels.stream().filter(x -> {
            return x.getChannelId() == channel.getId().intValue();
        }).findFirst().get();

        //组装授权渠道信息把userchannel和原channel表两处数据合在一起，代入request
        UserChannelVO userChannelVO = new UserChannelVO();
        BeanUtils.copyProperties(userChannel, userChannelVO);
        userChannelVO.setChannel(channel);
        request.setUserChannelVO(userChannelVO);

        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
        //当渠道为dqpost时替换serviceCode
        if (channel.getChannelPlatformId() == 3) {
            request.setServiceCode(channel.getChannelCode());
        }
        //当渠道为easypost时
        if (channel.getChannelPlatformId() == 4) {
            request.setServiceCode(channel.getChannelCode());
        }
        request.setOrderType(OrderType.API);

        return baseShipService.createLabel(request);
    }

    @ApiOperation("获取渠道")
    @RequestMapping(value = "/channel", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<GetChannelResult>> getUserChannel() {

        Integer userId = ApiUserUtil.getUserId();
        // Integer userId = 3;

        List<UserChannelVO> onChannels = channelService.getOnChannels(userId, "", null);
        if (CollectionUtil.isNotEmpty(onChannels)) {
            Set<Channel> channelSet = onChannels.stream().map(UserChannelVO::getChannel).collect(Collectors.toSet());
            Map<Integer, ChannelCarrier> channelCarrierMap = channelCarrierService.list(new LambdaQueryWrapper<ChannelCarrier>()
                            .in(ChannelCarrier::getId, onChannels.stream().map(UserChannelVO::getChannel).map(Channel::getChannelCarrierId).collect(Collectors.toSet())))
                    .stream().collect(Collectors.toMap(ChannelCarrier::getId, Function.identity()));


            List<GetChannelResult> channels = channelSet.stream().map(x -> {
                GetChannelResult channelResult = new GetChannelResult();
                channelResult.setChannelName(x.getChannelName());
                ChannelCarrier channelCarrier = channelCarrierMap.get(x.getChannelCarrierId());
                if (channelCarrier != null) {
                    channelResult.setCarrierCode(channelCarrier.getChannelCarrierCode());
                }
                // channel.setChannelId(x.getChannelId());
                return channelResult;
            }).collect(Collectors.toList());
            return CommonResult.success(channels);
        }
        return CommonResult.success();
    }


    @ApiOperation("获取面单")
    @RequestMapping(value = "/label/get/{objectId}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<CreateLabelResponse.Data> getLabel(@PathVariable String objectId) {

        // 调用接口
        log.info("获取面单"+JSONUtil.toJsonStr(objectId));
        Integer userId = ApiUserUtil.getUserId();
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                 .and(wp -> {
                    wp.eq(Order::getObjectId,objectId).or().eq(Order::getOrderNo, objectId);
                })
                .eq(Order::getUserId, userId)
        );

        if (order == null) {
            return CommonResult.failed("no order found");
        }

        Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>().eq(Channel::getId, order.getChannelId()));
        if (channel == null) {
            return CommonResult.failed("no channel found");
        }

        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
        return baseShipService.getLabel(objectId);
    }

    @ApiOperation("取消面单")
    @RequestMapping(value = "/label/cancel", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult createLabel(@RequestBody CancelLabelRequest request) {
        log.info("API取消面单"+JSONUtil.toJsonStr(request));
        String objectId = request.getObjectId();
        Integer userId = ApiUserUtil.getUserId();
        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getObjectId, objectId)
                .eq(Order::getUserId, userId)
        );

        if (order == null) {
            return CommonResult.failed("order not found");
        }
        Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>().eq(Channel::getId, order.getChannelId()));
        if (channel == null) {
            return CommonResult.failed("channel used in order now not found");
        }

        // 调用接口
        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
        CommonResult commonResult = baseShipService.cancelLabel(objectId,order.getOrderNo());
        if (commonResult.isSucess()) {
            Order updateOrder = new Order();
            updateOrder.setObjectId(objectId);
            updateOrder.setId(order.getId());
            updateOrder.setStatus(OrderStatus.CANCEL_ING.getValue());
            updateOrder.setCancelTime(LocalDateTime.now());
            orderService.updateById(updateOrder);
        }
        return commonResult;
    }

    @ApiOperation("跟踪")
    @RequestMapping(value = "/label/tracking", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<TrackingResponse.TrackingData> tracking(@RequestBody TrackingRequest request) {
        // 调用接口
        String trackingNumber = request.getTrackingNumber();
        log.info(JSONUtil.toJsonStr(trackingNumber));
        Integer userId = ApiUserUtil.getUserId();
        OrderParcel orderParcel = orderParcelService.getOne(new LambdaQueryWrapper<OrderParcel>()
                .eq(OrderParcel::getUserId, userId)
                .eq(OrderParcel::getTrackingNumber, trackingNumber)
        );

        if (orderParcel == null) {
            return CommonResult.failed("orderParcel not found ");
        }
        Order order = orderService.getById(orderParcel.getOrderId());

        Channel channel = channelService.getOne(new LambdaQueryWrapper<Channel>().eq(Channel::getId, order.getChannelId()));
        if (channel == null) {
            return CommonResult.failed("channel not found ");
        }


        BaseShipService baseShipService = shipFactory.getBaseShipService(channel);
        return baseShipService.tracking(trackingNumber);

    }


    @ApiOperation("地址校验")
    @RequestMapping(value = "/address/validate", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult<TrackingResponse.TrackingData> validate(@RequestBody EstimateRateRequest.Address request) {

        // 调用接口
        log.info(JSONUtil.toJsonStr(request));
        String strResult = ezeeShipUtil.validateAddress(request);
        if (StringUtils.isNotBlank(strResult)) {
            TrackingResponse trackingResponse = JSONUtil.toBean(strResult, TrackingResponse.class);
            if (!"OK".equals(trackingResponse.getResult())) {
                return CommonResult.failed(trackingResponse.getMessage());
            } else {
                return CommonResult.success(trackingResponse.getData());
            }
        }

        return CommonResult.success();
    }
}

